package nl.Viewer.ViewManager.Views;

import java.nio.Buffer;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLJPanel;
import javax.media.opengl.glu.GLU;

import nl.Viewer.SerializedObjects.Parameter;
import nl.Viewer.ViewManager.Controllers.GenericController;
import nl.Viewer.ViewManager.Objects.RendererHelperFunctions;

/**
 * This is the implementation of a View to show the orientation of a plane in a bounding box
 * 
 * @author Jeroen Verschuur
 * 
 */

public class JOGLPlaneOrientationView extends GenericView implements GLEventListener {
   private static final long serialVersionUID          = -2958949544243327703L;

   /********************************************************************************************************************
    * 
    * Interaction properties
    * 
    */

   /* viewing parameters */
   protected double          viewUp[]                  = new double[] { 0.0, 1.0, 0.0 };
   protected double          center[]                  = new double[] { 0.0, 0.0, 0.0 };
   protected double          eye[]                     = new double[] { 0.0, 0.0, 30.0 };
   protected double          viewAngle                 = 30.0;
   protected double          clippingRange[]           = new double[] { 0.1, 1000.0 };

   protected Parameter       CutPlaneNormal;
   protected Parameter       CutPlaneOrigin;
   protected Parameter       SourceObjectBounds;

   protected double          cutPlaneNormalValue[]     = new double[] { 0.0, 0.0, 1.0 };
   protected double          cutPlaneOriginValue[]     = new double[] { 0.0, 0.0, 0.0 };
   protected double          sourceObjectBoundsValue[] = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };

   private boolean           parametersRetrieved       = false;

   /********************************************************************************************************************
    * 
    */

   protected int             panelWidth                = 0, panelHeight = 0;
   protected double          aspect                    = 1.0;
   protected GLU             glu                       = new GLU();
   protected Buffer          buf                       = null;

   protected float           minScale                  = 1;
   protected float           scale                     = 1;
   protected float           imageCenterX              = 0, imageCenterY = 0;

   public JOGLPlaneOrientationView( GenericController parent ) {
      super( parent );
      this.renderPanel = new GLJPanel( getCapabilities() );

      parent.getDataModel().addDataStoreModelChangeListener( this );
      parent.getDataModel().addParameterModelChangeListener( this );

      ( (GLJPanel)renderPanel ).addGLEventListener( this );
   }

   public static GLCapabilities getCapabilities() {
      GLCapabilities c = new GLCapabilities();
      c.setDoubleBuffered( true );
      return c;
   }

   /**
    * Called by drawable to indicate mode or device has changed
    */
   public void displayChanged( GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged ) {
   }

   /**
    * Called after OpenGL is init'ed
    */
   public void init( GLAutoDrawable drawable ) {
      GL gl = drawable.getGL();
      // set erase color
      gl.glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); // white
      // set drawing color and point size
      gl.glColor3f( 0.0f, 0.0f, 0.0f );
      gl.glPointSize( 1.0f ); // a 'dot' is 4 by 4 pixel

      // enable alpha mask (import from gif sets alpha bits)
      gl.glBlendFunc( GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA );
      gl.glEnable( GL.GL_BLEND );
   }

   /**
    * Called to indicate the drawing surface has been moved and/or resized
    */
   public void reshape( GLAutoDrawable drawable, int x, int y, int width, int height ) {
      GL gl = drawable.getGL();
      gl.glViewport( 0, 0, width, height );
      gl.glMatrixMode( GL.GL_PROJECTION );
      gl.glLoadIdentity();
      panelWidth = width;
      panelHeight = height;
      aspect = (double)panelWidth / (double)panelHeight;
      glu.gluOrtho2D( 0.0, width, 0.0, height );
   }

   public void display() {
      ( (GLJPanel)renderPanel ).display();
   }

   /**
    * Called by drawable to initiate drawing
    */
   public void display( GLAutoDrawable drawable ) {
      GL gl = drawable.getGL();
      gl.glClear( GL.GL_COLOR_BUFFER_BIT );

      if( SourceObjectBounds != null ) {
         double viewAngle = 30;
         Double[] eye = new Double[ 3 ];
         Double[] center = new Double[ 3 ];
         Double[] viewUp = new Double[] { 0.0, 1.0, 0.0 };

         RendererHelperFunctions.resetCamera( viewAngle, sourceObjectBoundsValue, eye, center );

         // 3D if interacting
         gl.glMatrixMode( GL.GL_PROJECTION );
         gl.glLoadIdentity();
         glu.gluPerspective( viewAngle / 1, aspect, 0.0001/* clippingRange[0] */, 10000.0 /* clippingRange[1] */);

         gl.glMatrixMode( GL.GL_MODELVIEW );
         gl.glLoadIdentity();

         glu.gluLookAt( eye[0], eye[1], eye[2], center[0], center[1], center[2], viewUp[0], viewUp[1], viewUp[2] );

         drawPlane( gl );

      }

      gl.glFlush();
      ( (GLJPanel)renderPanel ).swapBuffers();
   }

   public synchronized void drawPlane( GL gl ) {
      RendererHelperFunctions.drawBox( gl, sourceObjectBoundsValue[0], sourceObjectBoundsValue[1],
            sourceObjectBoundsValue[2], sourceObjectBoundsValue[3], sourceObjectBoundsValue[4],
            sourceObjectBoundsValue[5] );

      // calculate (1-6) intersections with bounding box and draw them
      ArrayList<double[]> sortedVertices =
         RendererHelperFunctions.planeToBoundingBoxIntersection( cutPlaneNormalValue, cutPlaneOriginValue,
               sourceObjectBoundsValue );

      gl.glColor4f( 1, 1, 1, 1 );
      gl.glLineWidth( 1 );
      gl.glPolygonMode( GL.GL_FRONT_AND_BACK, GL.GL_FILL );
      gl.glEnable( GL.GL_POLYGON_SMOOTH );

      gl.glBegin( GL.GL_POLYGON );

      for( int i = 0; i < sortedVertices.size(); i++ ) {
         double[] v = sortedVertices.get( i );
         gl.glVertex3d( v[0], v[1], v[2] );

         if( i < sortedVertices.size() - 1 ) {
            v = sortedVertices.get( i + 1 );
         } else {
            v = sortedVertices.get( 0 );
         }
         gl.glVertex3d( v[0], v[1], v[2] );
      }

      gl.glEnd();

   }

   /*
    * Parameter methods
    * 
    */
   public synchronized void parameterValueChanged( Parameter p ) {
      synchronizeParameterValues();
   }

   public synchronized void dataStoreChanged( boolean newDataStore ) {
      getAllParameters();
   }

   private void getAllParameters() {
      if( parametersRetrieved )
         return;

      ConcurrentHashMap<String, Parameter> parameters = parent.getParameters();

      CutPlaneNormal = parameters.get( "CutPlaneNormal" );
      if( CutPlaneNormal == null )
         System.out.println( this.getClass() + " expected parameter CutPlaneNormal to exist on the server" );

      CutPlaneOrigin = parameters.get( "CutPlaneOrigin" );
      if( CutPlaneOrigin == null )
         System.out.println( this.getClass() + " expected parameter CutPlaneOrigin to exist on the server" );

      SourceObjectBounds = parameters.get( "SourceObjectBounds" );
      if( SourceObjectBounds == null )
         System.out.println( this.getClass() + " expected parameter SourceObjectBounds to exist on the server" );

      System.out.println( "getAllParameters" );

      parametersRetrieved = true;
   }

   private void synchronizeParameterValues() {
      if( SourceObjectBounds != null )
         sourceObjectBoundsValue = GenericView.objectToDoubleArray( SourceObjectBounds.getParameterValue() );

      if( CutPlaneNormal != null )
         cutPlaneNormalValue = GenericView.objectToDoubleArray( CutPlaneNormal.getParameterValue() );

      if( CutPlaneOrigin != null )
         cutPlaneOriginValue = GenericView.objectToDoubleArray( CutPlaneOrigin.getParameterValue() );
      
      super.synchronizeViews();
   }
}
